/*
 * DenseMatrix.cs
 *
 * Copyright (c) 2005, dnAnalytics. All rights reserved.
 */
#region Using Directives
using System;
using System.Text;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using dnAnalytics.Resources;
using dnAnalytics.Exceptions;
using dnAnalytics.LinearAlgebra.Decomposition;
#endregion Using Directives

namespace dnAnalytics.LinearAlgebra
{
    /// <summary>
    /// The matrix class.
    /// </summary>
    [Serializable]
    internal sealed class DenseMatrix : Matrix
    {
        #region Fields

        /// <summary>
        /// Internal data for matrix.
        /// <remarks>The matrix data is stored using a column-major layout.</remarks>
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        internal double[] data;
        private readonly ISimplifiedBlas blas;
        internal readonly ISimplifiedLapack lapack;
        #endregion Fields

        #region Constructors
        /// <summary>
        /// Creates an rows by columns <b>DenseMatrix</b>. The matrix values are the set to zero.
        /// </summary>
        /// <param name="order">The order of the new matrix.</param>
        /// <param name="useNativeLibrary">Use native BLAS and LAPACK routines.</param>
        public DenseMatrix(int order, bool useNativeLibrary)
            : this(order, order, useNativeLibrary)
        {
            data = new double[order * order];
        }

        /// <summary>
        /// Creates an rows by columns <b>DenseMatrix</b>. The matrix values are the set to zero.
        /// </summary>
        /// <param name="rows">The number of rows of the new matrix.</param>
        /// <param name="columns">The number of columns of the new matrix.</param>
        /// <param name="useNativeLibrary">Use native BLAS and LAPACK routines.</param>
        public DenseMatrix(int rows, int columns, bool useNativeLibrary)
            : base(rows, columns)
        {
            if (rows < 1)
            {
                throw new ArgumentException(Strings.NotPositiveException, "rows");
            }
            if (columns < 1)
            {
                throw new ArgumentException(Strings.NotPositiveException, "columns");
            }
            if (useNativeLibrary)
            {
                blas = new NativeBlas();
                lapack = new NativeLapack();
            }
            else
            {
                blas = new ManagedBlas();
                lapack = new ManagedLapack();
            }
            data = new double[rows * columns];
        }

        /// <summary>
        /// Creates a rows by columns <b>DenseMatrix</b>. The matrix values are the set to the given
        /// value.
        /// </summary>
        /// <param name="rows">The number of rows of the new matrix.</param>
        /// <param name="columns">The number of columns of the new matrix.</param>
        /// <param name="value">The value to set all elements of the matrix to.</param>
        /// <param name="useNativeLibrary">Use native BLAS and LAPACK routines.</param>
        public DenseMatrix(int rows, int columns, double value, bool useNativeLibrary)
            : this(rows, columns, useNativeLibrary)
        {
            for (int i = 0; i < data.Length; i++)
            {
                data[i] = value;
            }
        }

        /// <summary>
        /// Creates a <b>DenseMatrix</b> from another <b>DenseMatrix</b> (a copy constructor).
        /// </summary>
        /// <param name="matrix">The matrix to copy.</param>
        internal DenseMatrix(DenseMatrix matrix) : this(matrix, Settings.UseNativeLibrary) { }

        /// <summary>
        /// Creates a <b>DenseMatrix</b> from another <b>DenseMatrix</b> (a copy constructor).
        /// </summary>
        /// <param name="matrix">The matrix to copy.</param>
        /// <param name="useNativeLibrary">Use native BLAS and LAPACK routines.</param>
        internal DenseMatrix(DenseMatrix matrix, bool useNativeLibrary)
            : this(matrix.Rows, matrix.Columns, useNativeLibrary)
        {
            if (matrix == null)
            {
                throw new ArgumentNullException("matrix", Strings.NullParameterException);
            }

            data = new double[Rows * Columns];
            Buffer.BlockCopy(matrix.data, 0, data, 0, matrix.data.Length * Constants.SizeOfDouble);
        }


        /// <summary>
        /// Converts a Matrix to DenseMatrix
        /// </summary>
        internal DenseMatrix(Matrix matrix)
            : this(matrix.Rows, matrix.Columns, Settings.UseNativeLibrary)
        {
            if (matrix == null)
            {
                throw new ArgumentNullException("matrix", Strings.NullParameterException);
            }

            data = new double[Rows * Columns];

            //using enumerators since they will be more efficient for copying sparse matrices
            foreach (KeyValuePair<int, Vector> column in matrix.ColumnEnumerator())
            {
                foreach (KeyValuePair<int, double> element in column.Value)
                {
                    this.ValueAt(element.Key, column.Key, element.Value);
                }
            }
        }

        #endregion Constructors

        #region Properties

        /// <summary>
        /// Set or get an element of the matrix.
        /// </summary>
        /// <param name="row">The row index.</param>
        /// <param name="column">The column index.</param>
        /// <returns>A specific element in the matrix.</returns>
        /// <exception cref="ArgumentOutOfRangeException">If either the row or column index is outside the 
        /// the dimensions of this matrix.</exception>
        public override double this[int row, int column]
        {
            get
            {
                if (row >= this.Rows || row < 0)
                {
                    throw new ArgumentOutOfRangeException("row", Strings.OutOfRangeException);
                }
                if (column >= this.Columns || column < 0)
                {
                    throw new ArgumentOutOfRangeException("column", Strings.OutOfRangeException);
                }
                return data[column * Rows + row];
            }
            set
            {
                if (row >= this.Rows || row < 0)
                {
                    throw new ArgumentOutOfRangeException("row", Strings.OutOfRangeException);
                }
                if (column >= this.Columns || column < 0)
                {
                    throw new ArgumentOutOfRangeException("column", Strings.OutOfRangeException);
                }
                data[column * Rows + row] = value;
            }
        }

        #endregion Properties

        #region Protected Methods
        /// <summary>
        /// Retrieves the value at the given indices. Note: This method is not ranged checked. If the
        /// indices are outside the dimensions of the matrix, it may return an incorrect value or throw
        /// an <see cref="IndexOutOfRangeException"/>.
        /// </summary>
        /// <param name="row">The row index.</param>
        /// <param name="column">The column index.</param>
        /// <returns>The value at the given indices.</returns>
        /// <exception cref="IndexOutOfRangeException">If <paramref name="row"/> * <paramref name="column"/> is
        /// greater than <b>DenseMatrix.Rows * DenseMatrix.Columns</b></exception>.
        protected internal override double ValueAt(int row, int column)
        {
            return data[column * Rows + row];
        }

        /// <summary>
        /// Set the value at the given indices to the given value. Note: This method is not ranged checked. If the
        /// indices are outside the dimensions of the matrix, it may set an incorrect element or throw
        /// an <see cref="IndexOutOfRangeException"/>.
        /// </summary>
        /// <param name="row">The row index.</param>
        /// <param name="column">The column index.</param>
        /// <param name="value">The value to set.</param>
        /// <exception cref="IndexOutOfRangeException">If <paramref name="row"/> * <paramref name="column"/> is
        /// greater than <b>DenseMatrix.Rows * DenseMatrix.Columns</b></exception>.
        protected internal override void ValueAt(int row, int column, double value)
        {
            data[column * Rows + row] = value;
        }

        protected override Matrix CreateMatrix(int rows, int columns)
        {
            return MatrixBuilder.CreateMatrix(rows, columns, MatrixType.Dense);
        }
        #endregion Protected Methods

        #region Public Methods

        /// <summary>Calculates the Frobenius norm of this matrix.</summary>
        /// <returns>The Frobenius norm of this matrix.</returns>
        public override double FrobeniusNorm()
        {
            return lapack.Norm('F', Rows, Columns, data);
        }

        /// <summary>Calculates the infinity norm of this matrix.</summary>
        /// <returns>The infinity norm of this matrix.</returns>      
        public override double InfinityNorm()
        {
            return lapack.Norm('I', Rows, Columns, data);
        }

        /// <summary>Calculates the L1 norm of this matrix.</summary>
        /// <returns>The L1 norm of this matrix.</returns>
        public override double L1Norm()
        {
            return lapack.Norm('1', Rows, Columns, data);
        }

        /// <summary>
        /// Sets all values to zero.
        /// </summary>
        public override void Clear()
        {
            Array.Clear(data, 0, data.Length);
        }

        /// <summary>
        /// Copies the elements of this matrix to the given matrix.
        /// </summary>
        /// <param name="target">The matrix to copy values into.</param>
        /// <exception cref="ArgumentNullException">If target is <b>null</b>.</exception>
        /// <exception cref="NotConformableException">If target is not the same size as this matrix.</exception>
        public override void CopyTo(Matrix target)
        {
            if (target == null)
            {
                throw new ArgumentNullException("target", Strings.NullParameterException);
            }

            if (Rows != target.Rows || Columns != target.Columns)
            {
                throw new NotConformableException("target", Strings.ParameterNotConformable);
            }

            DenseMatrix dense = target as DenseMatrix;
            if (dense == null)
            {
                base.CopyTo(target);
            }
            else
            {
                Buffer.BlockCopy(data, 0, dense.data, 0, data.Length * Constants.SizeOfDouble);
            }
        }

        /// <summary>
        /// Copies the given <see cref="Range"/> of a column into a vector.
        /// </summary>
        /// <param name="index">The column to copy.</param>
        /// <param name="range">The <see cref="Range"/> of elements to copy from the requested column.</param>
        /// <returns>A <see cref="Vector"/> containing the copied elements.</returns>
        /// <exception cref="ArgumentOutOfRangeException">If <paramref name="Range"/> specifies a range
        /// outside the column's range. </exception>
        /// <exception cref="ArgumentOutOfRangeException">If <paramref name="index"/> is negative,
        /// or greater than or equal to the number of columns.</exception>        
        public override Vector Column(int index, Range range)
        {
            if (index >= Columns || index < 0)
            {
                throw new ArgumentOutOfRangeException("index", Strings.OutOfRangeException);
            }
            if (range.Start >= Rows || range.End >= Rows)
            {
                throw new ArgumentOutOfRangeException("range", Strings.OutOfRangeException);
            }

            DenseVector ret = new DenseVector((range.End - range.Start) / range.Stride + 1, Settings.UseNativeLibrary);
            this.Column(index, range, ret);
            return ret;
        }

        /// <summary>
        /// Copies the given <see cref="Range"/> of a row into an <see cref="Vector"/>.
        /// </summary>
        /// <param name="index">The column to copy.</param>
        /// <param name="range">The <see cref="Range"/> of elements to copy.</param>
        /// <returns>A <see cref="Vector"/> containing the copied elements.</returns>
        /// <exception cref="ArgumentOutOfRangeException">If <paramref name="Range"/> specifies a range
        /// outside the row's range. </exception>
        /// <exception cref="ArgumentOutOfRangeException">If <paramref name="index"/> is negative,
        /// or greater than or equal to the number of rows.</exception>    
        public override Vector Row(int index, Range range)
        {
            if (index >= Rows || index < 0)
            {
                throw new ArgumentOutOfRangeException("index", Strings.OutOfRangeException);
            }
            if (range.Start >= this.Columns || range.End >= this.Columns)
            {
                throw new ArgumentOutOfRangeException("range", Strings.OutOfRangeException);
            }
            DenseVector ret = new DenseVector((range.End - range.Start) / range.Stride + 1, Settings.UseNativeLibrary);
            Row(index, range, ret);
            return ret;
        }

        /// <summary>
        /// Copies the transpose of this matrix into the result matrix.
        /// </summary>
        /// <param name="result">The result of the transpose.</param>
        /// <exception cref="ArgumentNullException">If the result matrix is <b>null</b>.</exception> 
        /// <exception cref="NotConformableException">If the result matrix's dimensions are not <b>this.Columns x this.Rows</b>.</exception>
        public override void Transpose(Matrix result)
        {
            if (result == null)
            {
                throw new ArgumentNullException("result", Strings.NullParameterException);
            }
            if (this.Columns != result.Rows || this.Rows != result.Columns)
            {
                throw new NotConformableException("result", Strings.ParameterNotConformable);
            }

            DenseMatrix dense = result as DenseMatrix;
            if (dense == null)
            {
                base.Transpose(result);
            }
            else
            {
                for (int j = 0; j < Columns; j++)
                {
                    int index = j * Rows;
                    for (int i = 0; i < Rows; i++)
                    {
                        dense.data[i * Columns + j] = data[index + i];
                    }
                }
            }
        }

        /// <summary>
        /// Returns a new matrix containing the lower triangle of this matrix.
        /// </summary>
        /// <returns>The lower triangle of this matrix.</returns>        
        public override Matrix LowerTriangle()
        {
            DenseMatrix ret = new DenseMatrix(Rows, Columns, Settings.UseNativeLibrary);
            //not using LowerTriangle(ret) to avoid the extra clear call.

            for (int j = 0; j < Columns; j++)
            {
                int tmp = j * Rows;
                for (int i = 0; i < Rows; i++)
                {
                    if (i >= j)
                    {
                        ret.data[tmp + i] = data[tmp + i];
                    }
                }
            }

            return ret;
        }

        /// <summary>
        /// Puts the the lower triangle of this matrix into the result matrix.
        /// </summary>
        /// <param name="result">Where to store the the lower triangle.</param>
        /// <exception cref="ArgumentNullException">If the result matrix is <b>null</b>.</exception>
        /// <exception cref="NotConformableException">If the result matrix's dimensions are not the same as this matrix.</exception>
        public override void LowerTriangle(Matrix result)
        {
            if (result == null)
            {
                throw new ArgumentNullException("result", Strings.NullParameterException);
            }

            if (result.Rows != Rows || result.Columns != Columns)
            {
                throw new NotConformableException("result", Strings.ParameterNotConformable);
            }

            DenseMatrix dense = result as DenseMatrix;
            if (dense == null)
            {
                result.Clear();
                for (int j = 0; j < Columns; j++)
                {
                    int tmp = j * Rows;
                    for (int i = 0; i < Rows; i++)
                    {
                        if (i >= j)
                        {
                            dense.data[tmp + i] = data[tmp + i];
                        }
                    }
                }
            }
            else
            {
                base.LowerTriangle(result);
            }
        }

        /// <summary>
        /// Returns a new matrix containing the lower triangle of this matrix. The new matrix
        /// does not contain the diagonal elements of this matrix.
        /// </summary>
        /// <returns>The lower triangle of this matrix.</returns>
        public override Matrix StrictlyLowerTriangle()
        {
            DenseMatrix ret = new DenseMatrix(Rows, Columns, Settings.UseNativeLibrary);
            //not using StrictlyLowerTriangle(ret) to avoid the extra clear call.

            for (int j = 0; j < Columns; j++)
            {
                int tmp = j * Rows;
                for (int i = 0; i < Rows; i++)
                {
                    if (i > j)
                    {
                        ret.data[tmp + i] = data[tmp + i];
                    }
                }
            }

            return ret;
        }

        /// <summary>
        /// Puts the the lower triangle of this matrix into the result matrix. The result matrix doesn't 
        /// contain the diagonal elements of this matrix.
        /// </summary>
        /// <param name="result">Where to store the the lower triangle.</param>
        /// <exception cref="ArgumentNullException">If the result matrix is <b>null</b>.</exception>  
        /// <exception cref="NotConformableException">If the result matrix's dimensions are not the same as this matrix.</exception>
        public override void StrictlyLowerTriangle(Matrix result)
        {
            if (result == null)
            {
                throw new ArgumentNullException("result", Strings.NullParameterException);
            }

            if (result.Rows != Rows || result.Columns != Columns)
            {
                throw new NotConformableException("result", Strings.ParameterNotConformable);
            }


            DenseMatrix dense = result as DenseMatrix;
            if (dense == null)
            {
                result.Clear();
                for (int j = 0; j < Columns; j++)
                {
                    int tmp = j * Rows;
                    for (int i = 0; i < Rows; i++)
                    {
                        if (i > j)
                        {
                            dense.data[tmp + i] = data[tmp + i];
                        }
                    }
                }
            }
            else
            {
                base.StrictlyLowerTriangle(result);
            }
        }

        /// <summary>
        /// Puts the the upper triangle of this matrix into the result matrix.
        /// </summary>
        /// <param name="result">Where to store the the upper triangle.</param>
        /// <exception cref="ArgumentNullException">If the result matrix is <b>null</b>.</exception>
        /// <exception cref="NotConformableException">If the result matrix's dimensions are not the same as this matrix.</exception>
        public override void UpperTriangle(Matrix result)
        {
            if (result == null)
            {
                throw new ArgumentNullException("result", Strings.NullParameterException);
            }

            if (result.Rows != Rows || result.Columns != Columns)
            {
                throw new NotConformableException("result", Strings.ParameterNotConformable);
            }

            DenseMatrix dense = result as DenseMatrix;
            if (dense == null)
            {
                result.Clear();
                for (int j = 0; j < Columns; j++)
                {
                    int tmp = j * Rows;
                    for (int i = 0; i < Rows; i++)
                    {
                        if (i <= j)
                        {
                            dense.data[tmp + i] = data[tmp + i];
                        }
                    }
                }
            }
            else
            {
                base.UpperTriangle(result);
            }
        }

        /// <summary>
        /// Returns a new matrix containing the upper triangle of this matrix.
        /// </summary>
        /// <returns>The upper triangle of this matrix.</returns>   
        public override Matrix UpperTriangle()
        {
            DenseMatrix ret = new DenseMatrix(Rows, Columns, Settings.UseNativeLibrary);
            //not using UpperTriangle(ret) to avoid the extra clear call.

            for (int j = 0; j < Columns; j++)
            {
                int tmp = j * Rows;
                for (int i = 0; i < Rows; i++)
                {
                    if (i <= j)
                    {
                        ret.data[tmp + i] = data[tmp + i];
                    }
                }
            }

            return ret;
        }

        /// <summary>
        /// Returns a new matrix containing the upper triangle of this matrix. The new matrix
        /// does not contain the diagonal elements of this matrix.
        /// </summary>
        /// <returns>The upper triangle of this matrix.</returns>
        public override Matrix StrictlyUpperTriangle()
        {
            DenseMatrix ret = new DenseMatrix(Rows, Columns, Settings.UseNativeLibrary);
            //not using StrictlyUpperTriangle(ret) to avoid the extra clear call.

            for (int j = 0; j < Columns; j++)
            {
                int tmp = j * Rows;
                for (int i = 0; i < Rows; i++)
                {
                    if (i < j)
                    {
                        ret.data[tmp + i] = data[tmp + i];
                    }
                }
            }

            return ret;
        }

        /// <summary>
        /// Puts the the upper triangle of this matrix into the result matrix. The result matrix doesn't 
        /// contain the diagonal elements of this matrix.
        /// </summary>
        /// <param name="result">Where to store the the upper triangle.</param>
        /// <exception cref="ArgumentNullException">If the result matrix is <b>null</b>.</exception>          
        /// <exception cref="NotConformableException">If the result matrix's dimensions are not the same as this matrix.</exception>
        public override void StrictlyUpperTriangle(Matrix result)
        {
            if (result == null)
            {
                throw new ArgumentNullException("result", Strings.NullParameterException);
            }

            if (result.Rows != Rows || result.Columns != Columns)
            {
                throw new NotConformableException("result", Strings.ParameterNotConformable);
            }

            DenseMatrix dense = result as DenseMatrix;
            if (dense == null)
            {
                result.Clear();
                for (int j = 0; j < Columns; j++)
                {
                    int tmp = j * Rows;
                    for (int i = 0; i < Rows; i++)
                    {
                        if (i < j)
                        {
                            dense.data[tmp + i] = data[tmp + i];
                        }
                    }
                }
            }
            else
            {
                base.StrictlyUpperTriangle(result);
            }
        }

        /// <summary>
        /// Copies a sub-matrix of this matrix into a given matrix.
        /// </summary>
        /// <param name="rowRange">The <see cref="Range"/> of rows to copy into the given matrix.</param>
        /// <param name="columnRange">The <see cref="Range"/> of columns to copy into the given matrix.</param>
        /// <param name="result">The matrix to copy the sub-matrix into.</param>
        /// <exception cref="ArgumentOutOfRangeException">If either <paramref name="rowRange"/> or <paramref name="columnRange"/> 
        /// specify a <see cref="Range"/> outside the dimensions of the matrix.</exception>
        /// <exception cref="ArgumentNullException">If the result matrix is <b>null</b>.</exception>
        /// <exception cref="NotConformableException">If the result matrix's dimensions are not the same as those defined by the <b>Ranges</b>.</exception>
        public override void SubMatrix(Range rowRange, Range columnRange, Matrix result)
        {
            if (rowRange.Start >= Rows || rowRange.End >= Rows)
            {
                throw new ArgumentOutOfRangeException("rowRange", Strings.OutOfRangeException);
            }

            if (columnRange.Start >= Columns || columnRange.End >= Columns)
            {
                throw new ArgumentOutOfRangeException("columnRange", Strings.OutOfRangeException);
            }

            if (result == null)
            {
                throw new ArgumentNullException("result", Strings.NullParameterException);
            }

            DenseMatrix dense = result as DenseMatrix;
            if (dense == null)
            {
                base.SubMatrix(rowRange, columnRange, result);
            }
            else
            {
                int m = (rowRange.End - rowRange.Start) / rowRange.Stride + 1;
                int n = (columnRange.End - columnRange.Start) / columnRange.Stride + 1;
                if (result.Rows != m || result.Columns != n)
                {
                    throw new NotConformableException("result", Strings.ParameterNotConformable);
                }

                for (int j = columnRange.Start, jj = 0; j <= columnRange.End; j += columnRange.Stride, jj++)
                {
                    int index1 = jj * m;
                    int index2 = j * Rows;
                    for (int i = rowRange.Start, ii = 0; i <= rowRange.End; i += rowRange.Stride, ii++)
                    {
                        dense.data[index1 + ii] = data[index2 + i];
                    }
                }
            }

        }

        /// <summary>
        /// Returns this matrix as a multidimensional array.
        /// </summary>
        /// <returns>A multidimensional containing the values of this matrix.</returns>        
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1814:PreferJaggedArraysOverMultidimensional", MessageId = "Body"), SuppressMessage("Microsoft.Performance", "CA1814:PreferJaggedArraysOverMultidimensional", MessageId = "Return")]
        public override double[,] ToArray()
        {
            double[,] ret = new double[Rows, Columns];

            for (int j = 0; j < Columns; j++)
            {
                int index = j * Rows;
                for (int i = 0; i < Rows; i++)
                {
                    ret[i, j] = data[index + i];
                }
            }
            return ret;
        }

        /// <summary>
        /// Returns the matrix's elements as an array with data laid column wise.
        /// </summary>
        /// <example><pre>
        /// 1, 2, 3
        /// 4, 5, 6  will be returned as  1, 4, 7, 2, 5, 8, 3, 6, 9
        /// 7, 8, 9
        /// </pre></example>
        /// <returns>An array containing the matrix's elements.</returns>      
        public override double[] ToColumnWiseArray()
        {
            double[] ret = new double[Rows * Columns];
            Buffer.BlockCopy(data, 0, ret, 0, data.Length * Constants.SizeOfDouble);
            return ret;
        }

        /// <summary>
        /// Returns the matrix's elements as an array with data laid row wise.
        /// </summary>
        /// <example><pre>
        /// 1, 2, 3
        /// 4, 5, 6  will be returned as  1, 2, 3, 4, 5, 6, 7, 8, 9
        /// 7, 8, 9
        /// </pre></example>
        /// <returns>An array containing the matrix's elements.</returns>
        public override double[] ToRowWiseArray()
        {
            double[] ret = new double[Rows * Columns];
            for (int j = 0; j < Columns; j++)
            {
                int index = j * Rows;
                for (int i = 0; i < Rows; i++)
                {
                    ret[i * Columns + j] = data[index + i];
                }
            }

            return ret;
        }

        /// <summary>
        /// Adds a scalar to each element in the matrix overwriting the values of this matrix.
        /// </summary>
        /// <param name="scalar">The scalar to add.</param>        
        public override void Add(double scalar)
        {
            blas.Add(data, scalar);
        }

        /// <summary>
        /// Adds another matrix to this matrix overwriting the values of this matrix.
        /// </summary>
        /// <param name="other">The matrix to add to this matrix.</param>
        /// <exception cref="ArgumentNullException">If the other matrix is <b>null</b>.</exception>
        /// <exception cref="NotConformableException">If the two matrices don't have the same dimensions.</exception>
        public override void Add(Matrix other)
        {
            if (other == null)
            {
                throw new ArgumentNullException("other", Strings.NullParameterException);
            }

            if (other.Rows != Rows || other.Columns != Columns)
            {
                throw new NotConformableException("other", Strings.ParameterNotConformable);
            }

            DenseMatrix dense = other as DenseMatrix;
            if (dense == null)
            {
                base.Add(other);
            }
            else
            {
                blas.Add(data, dense.data);
            }
        }

        /// <summary>
        /// Negates each element of this matrix.
        /// </summary>        
        public override void Negate()
        {
            blas.Scale(-1, data);
        }

        /// <summary>
        /// Subtracts a scalar from each value in the matrix overwriting the values of this matrix.
        /// </summary>
        /// <param name="scalar">The scalar to subtract.</param>
        public override void Subtract(double scalar)
        {
            blas.Subtract(data, scalar);
        }

        /// <summary>
        /// Subtracts another matrix from this matrix overwriting the values of this matrix.
        /// </summary>
        /// <param name="other">The matrix to subtract.</param>
        /// <exception cref="ArgumentNullException">If the other matrix is <b>null</b>.</exception>
        /// <exception cref="NotConformableException">If the two matrices don't have the same dimensions.</exception>
        public override void Subtract(Matrix other)
        {
            if (other == null)
            {
                throw new ArgumentNullException("other", Strings.NullParameterException);
            }

            if (other.Rows != Rows || other.Columns != Columns)
            {
                throw new NotConformableException("other", Strings.ParameterNotConformable);
            }

            DenseMatrix dense = other as DenseMatrix;
            if (dense == null)
            {
                base.Subtract(other);
            }
            else
            {
                blas.Subtract(data, dense.data);
            }
        }

        /// <summary>
        /// Divides each element of the matrix by a scalar overwriting the values of this matrix.
        /// </summary>
        /// <param name="scalar">The scalar to divide by.</param>
        public override void Divide(double scalar)
        {
            blas.Scale(1 / scalar, data);
        }

        /// <summary>
        /// Multiplies each element of this matrix with a scalar overwriting the values of this matrix.
        /// </summary>
        /// <param name="scalar">The scalar to multiply with.</param>
        public override void Multiply(double scalar)
        {
            blas.Scale(scalar, data);
        }

        /// <summary>
        /// Multiplies this matrix with another matrix and places the results into the result matrix.
        /// </summary>
        /// <param name="other">The matrix to multiply with.</param>
        /// <param name="result">The result of the multiplication.</param>
        /// <exception cref="ArgumentNullException">If the other matrix is <b>null</b>.</exception>
        /// <exception cref="ArgumentNullException">If the result matrix is <b>null</b>.</exception>
        /// <exception cref="NotConformableException">If <b>this.Columns != other.Rows</b>.</exception>
        /// <exception cref="NotConformableException">If the result matrix's dimensions are not the this.Rows x other.Columns.</exception>
        public override void Multiply(Matrix other, Matrix result)
        {
            if (other == null)
            {
                throw new ArgumentNullException("other", Strings.NullParameterException);
            }

            if (Columns != other.Rows)
            {
                throw new NotConformableException("other", Strings.ParameterNotConformable);
            }

            if (result == null)
            {
                throw new ArgumentNullException("result", Strings.NullParameterException);
            }

            if (result.Rows != Rows || result.Columns != other.Columns)
            {
                throw new NotConformableException("result", Strings.ParameterNotConformable);
            }
            if (other.GetType() == typeof(DenseMatrix) && result.GetType() == typeof(DenseMatrix))
            {

                blas.Multiply(Rows, other.Columns, Columns, data, ((DenseMatrix)other).data, ((DenseMatrix)result).data);
            }
            else
            {
                base.Multiply(other, result);
            }
        }

        /// <summary>
        /// Multiples this matrix with a vector and places the results into the result matrix. Note the operation is vector * matrix,
        /// where the vector is treated as a row vector.
        /// </summary>
        /// <param name="leftSide">The vector to multiply with.</param>
        /// <param name="result">The result of the multiplication.</param>
        /// <exception cref="ArgumentNullException">If leftSide is <b>null</b>.</exception>
        /// <exception cref="ArgumentNullException">If the result matrix is <b>null</b>.</exception>
        /// <exception cref="NotConformableException">If <b>result.Count != this.Columns</b>.</exception>
        /// <exception cref="NotConformableException">If <b>this.Rows != leftSide.Count</b>.</exception>
        public override void LeftMultiply(Vector leftSide, Vector result)
        {
            if (leftSide == null)
            {
                throw new ArgumentNullException("rightSide", Strings.NullParameterException);
            }

            if (Rows != leftSide.Count)
            {
                throw new NotConformableException("leftSide", Strings.ParameterNotConformable);
            }

            if (result == null)
            {
                throw new ArgumentNullException("result", Strings.NullParameterException);
            }

            if (Columns != result.Count)
            {
                throw new NotConformableException("result", Strings.ParameterNotConformable);
            }

            if (leftSide.GetType() == typeof(DenseVector) && result.GetType() == typeof(DenseVector))
            {
                blas.Multiply(1, Columns, leftSide.Count, ((DenseVector)leftSide).data, data, ((DenseVector)result).data);
            }
            else
            {
                base.LeftMultiply(leftSide, result);
            }
        }

        public override Matrix Clone()
        {
            return new DenseMatrix(this);
        }

        #endregion Public Methods

    }
}
